#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>

#define UINT unsigned short
#define DWORD unsigned int
#define LONG unsigned int
#define WORD unsigned short

// 14
struct tagBITMAPFILEHEADER
{
    WORD bfType;      // 位图文件的类型，必须为BM(1-2字节）
    DWORD bfSize;     // 位图文件的大小，以字节为单位（3-6字节）
    WORD bfReserved1; // 位图文件保留字，必须为0(7-8字节）
    WORD bfReserved2; // 位图文件保留字，必须为0(9-10字节）
    DWORD bfOffBits;  // 位图数据的起始位置，以相对于位图（11-14字节）
    // 文件头的偏移量表示，以字节为单位
} __attribute__((packed));

// 40
struct tagBITMAPINFOHEADER
{
    DWORD biSize;    // 本结构所占用字节数（15-18字节）
    LONG biWidth;    // 位图的宽度，以像素为单位（19-22字节）    ------图片的宽度
    LONG biHeight;   // 位图的高度，以像素为单位（23-26字节）   ------图片的高度
    WORD biPlanes;   // 目标设备的级别，必须为1(27-28字节）
    WORD biBitCount; // 每个像素所需的位数，必须是1（双色），（29-30字节）  ---位深
    // 4(16色），8(256色）或24（真彩色）之一
    DWORD biCompression; // 位图压缩类型，必须是 0（不压缩），（31-34字节）
    // 1(BI_RLE8压缩类型）或2(BI_RLE4压缩类型）之一
    DWORD biSizeImage;    // 位图的大小，以字节为单位（35-38字节）   ---图片颜色数据的大小
    LONG biXPelsPerMeter; // 位图水平分辨率，每米像素数（39-42字节）
    LONG biYPelsPerMeter; // 位图垂直分辨率，每米像素数（43-46字节)
    DWORD biClrUsed;      // 位图实际使用的颜色表中的颜色数（47-50字节）
    DWORD biClrImportant; // 位图显示过程中重要的颜色数（51-54字节）
} __attribute__((packed));

int main()
{
    // =====================
    int fd_lcd;
    fd_lcd = open("/dev/fb0", O_RDWR);

    if (fd_lcd < 0)
    {
        printf("open lcd error: %s\n", strerror(errno));
        return -1;
    }
    printf("open lcd success\n");

    int (*lcd_mmap)[800] = mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd_lcd, 0);
    printf("mmap success\n");

    // =============================
    int fd_bmp = open("./201-200.bmp", O_RDONLY);
    if (fd_bmp < 0)
    {
        printf("open bmp error: %s\n", strerror(errno));
        return -1;
    }
    printf("open bmp success\n");

    // bmp头
    struct tagBITMAPFILEHEADER file_header;
    struct tagBITMAPINFOHEADER info_header;
    read(fd_bmp, &file_header, sizeof(file_header));
    read(fd_bmp, &info_header, sizeof(info_header));

    printf("file_header.bfType: %x\n", file_header.bfType);
    printf("file_header.bfSize: %d\n", file_header.bfSize);

    printf("info_header.biWidth: %d\n", info_header.biWidth);
    printf("info_header.biHeight: %d\n", info_header.biHeight);

    int width = info_header.biWidth;
    int height = info_header.biHeight;

    // bmp像素信息
    char buf_bmp[height][width * 3];
    memset(buf_bmp, 0, height * width * 3);
    // read(fd_bmp, buf_bmp, sizeof(buf_bmp));

    // 逐行读取  bmp图片每一行的字节数 必须是4的倍数
    for (int i = 0; i < height; i++)
    {
        read(fd_bmp, &buf_bmp[i], width * 3);
        // 9像素  9*3=27  差1个字节是4的倍数28  补1字节
        // 10像素  10*3=30  差2个字节是4的倍数32 补2字节
        lseek(fd_bmp, 4 - (width * 3) % 4 == 0 ? 0 : 4, SEEK_CUR);
    }

    int x = 200, y = 100; // 把图片显示到 xy坐标
    for (int row = 0; row < height; row++)
    {
        for (int col = 0; col < width; col++)
        {
            if (col + x < 800 && height - 1 - row + y < 480)
            {
                lcd_mmap[height - 1 - row + y][col + x] = buf_bmp[row][col * 3 + 0] << 0 | buf_bmp[row][col * 3 + 1] << 8 | buf_bmp[row][col * 3 + 2] << 16 | 0x00 << 24;
                // 解决上下颠倒 颜色顺序
            }
        }
    }
    // 关闭文件
    munmap(lcd_mmap, 800 * 480 * 4);
    close(fd_lcd);
    return 0;
}